#pragma warning(disable:4996)

#include "loirey_XMLparser.h"

using namespace loirey;

const char* CxmlTree::IGNORING_CHARS = "\t\r\n";
const string CxmlTree::STR_ROOT = "ROOT";

CxmlTreeNode::CxmlTreeNode()
{
	NotationID = -1;
	pNotationName = NULL;
	strContent = "";

	pParent = NULL;
	pFirstChild = NULL;
	pLastChild = NULL;
	pPreviousBrother = NULL;
	pNextBrother = NULL;
}

CxmlTreeNode::~CxmlTreeNode()
{
	if (pParent != NULL)
	{
		if (pPreviousBrother != NULL)
		{
			pPreviousBrother->pNextBrother = pNextBrother;
		}
		else
		{
			pParent->pFirstChild = pNextBrother;
			if (pParent->pLastChild == this)
				pParent->pLastChild = pNextBrother;
		}

		if (pNextBrother != NULL)
			pNextBrother->pPreviousBrother = pPreviousBrother;

		pParent = NULL;
	}
	CxmlTreeNode* pNode;
	CxmlTreeNode* qNode;
	for ( pNode = pFirstChild; pNode != NULL; )
	{
		qNode = pNode->pNextBrother;
		delete pNode;
		pNode = qNode;
	}
	pFirstChild = pLastChild = NULL;
}

void CxmlTreeNode::OutputNodeToStream(ostream& outStream, int StepInto)
{
	if (StepInto>0) outStream<<setw(StepInto)<<' ';
	outStream<<setiosflags(ios::left)<<setw(32-StepInto)<<*pNotationName<<"\t: "<<strContent<<endl;
}

void CxmlTreeNode::OutputTreeToStream(ostream& outStream, int StepInto)
{
	OutputNodeToStream(outStream,StepInto);
	StepInto += 1;
	for ( CxmlTreeNode* pNode = pFirstChild; pNode; pNode = pNode->pNextBrother )
		pNode->OutputTreeToStream(outStream, StepInto);
}

CxmlTreeNode* CxmlTreeNode::GetNewChild()
{
	CxmlTreeNode* pNode = new CxmlTreeNode();

	pNode->pParent = this;
	pNode->pFirstChild = NULL;
	pNode->pLastChild = NULL;
	pNode->pNextBrother = NULL;
	if (pLastChild == NULL)
	{
		pFirstChild = pLastChild = pNode;
		pNode->pPreviousBrother = NULL;
	}
	else
	{
		pLastChild->pNextBrother = pNode;
		pNode->pPreviousBrother = pLastChild;
	}
	pLastChild = pNode;

	return pNode;
}

CxmlTreeNode* CxmlTreeNode::GetNextNode_PreorderTraversal()
{
	CxmlTreeNode* pNode;
	if (pFirstChild != NULL)
		pNode = pFirstChild;
	else
	{
		pNode = this;
		while (pNode && pNode->pNextBrother==NULL)
			pNode = pNode->pParent;
		if (pNode)
			pNode = pNode->pNextBrother;
	}
	return pNode;
}

CxmlTreeNode* CxmlTreeNode::GetNextNode_PostorderTraversal()
{
	CxmlTreeNode* pNode;
	if (pNextBrother == NULL)
		pNode = pParent;
	else
	{
		pNode = pNextBrother;
		while (pNode->pFirstChild != NULL)
			pNode = pNode->pFirstChild;
	}
	return pNode;
}

CxmlTreeNode* CxmlTreeNode::GetNextChildWithNotation(int NotationID, CxmlTreeNode* pCurrentChild)
{
	CxmlTreeNode* ret;
	if (!pFirstChild || (pCurrentChild && pCurrentChild->pParent!=this))
		ret = NULL;
	else
	{
		if (!pCurrentChild)
			pCurrentChild = pFirstChild;
		else
			pCurrentChild = pCurrentChild->pNextBrother;
		while (pCurrentChild && pCurrentChild->NotationID!=NotationID)
			pCurrentChild = pCurrentChild->pNextBrother;
		ret = pCurrentChild;
	}
	return ret;
}

char CxmlTree::_NextChar()
{
	char ret = 0;
	if (_head>=_tail)
	{
		_head = 0;
		_tail = (int)fread(_buf, 1, BUF_SIZE, FIN);
		_buf[_tail] = 0;
	}
	fEOF = _head>=_tail;
	if (_head<_tail)
		ret = _buf[_head++];
	return ret;
}

char CxmlTree::_NextNonIgnoringChar()
{
	char ret = 0;
	const char* pIC;

	for (;;)
	{
		ret = _NextChar();
		if (fEOF)
			break;
		for ( pIC = IGNORING_CHARS; *pIC; ++pIC )
			if (ret == *pIC)
				break;
		if (!*pIC)
			break;
	}
	return ret;
}

int CxmlTree::_NextToken(string& DstString)
{
	int ret = 0;

	if (_CurrentChar == '<')
	{
		_CurrentChar = _NextNonIgnoringChar();
		if (_CurrentChar == '/')
		{
			ret = -1;
			_CurrentChar = _NextNonIgnoringChar();
		}
		else
			ret = 1;
	}
	else
		ret = 0;

	DstString = "";
	for ( ; !fEOF; _CurrentChar = _NextNonIgnoringChar() )
	{
		if (_CurrentChar == '\\')
		{
			_CurrentChar = _NextNonIgnoringChar();
			switch (_CurrentChar)
			{
				case 'T':
				case 't':
				{
					_CurrentChar = '\t';
					break;
				}
				case 'R':
				case 'r':
				{
					_CurrentChar = '\r';
					break;
				}
				case 'N':
				case 'n':
				{
					_CurrentChar = '\n';
					break;
				}
				case '\\':
				{
					_CurrentChar = '\\';
					break;
				}
				case '<':
				{
					_CurrentChar = '<';
					break;
				}
				case '/':
				{
					_CurrentChar = '/';
					break;
				}
				case '>':
				{
					_CurrentChar = '>';
					break;
				}
				default:
				{
					_CurrentChar = 0;
					break;
				}
			}
		}
		else if (_CurrentChar == '<')
		{
			if (ret == 0)
			{
				break;
			}
		}
		else if (_CurrentChar == '>')
		{
			if (ret != 0)
			{
				_CurrentChar = _NextNonIgnoringChar();
				break;
			}
		}
		if (_CurrentChar)
			DstString.push_back(_CurrentChar);
	}

	return ret;
}

void CxmlTree::myRelease()
{
	if (FIN != NULL)
	{
		fclose(FIN);
		FIN = NULL;
	}
	if (Root != NULL)
	{
		delete Root;
		Root = NULL;
	}
	NotationDict.Clear();
}

CxmlTree::CxmlTree()
{
	_buf = new char[BUF_SIZE+1];

	FIN = NULL;
	Root = NULL;
	myRelease();
}

CxmlTree::~CxmlTree()
{
	delete[] _buf;
	_buf = NULL;

	myRelease();
}

int CxmlTree::GetNotationID(string strNotationName)
{
	if (!fIsNotationCaseSensitive)
		CDataStructureTools::ToLowerCase(strNotationName);
	int ret = NotationDict.GetID(strNotationName, false);
	return ret;
}

void CxmlTree::myInit(string strFN_XML)
{
	myInit(strFN_XML, false, true);
}

void CxmlTree::myInit(string strFN_XML, bool fIsNotationCaseSensitive, bool fIsContentCaseSensitive)
{
	myRelease();

	FIN = fopen(strFN_XML.c_str(),"rb");
	_head = 0;
	_tail = 0;

	_CurrentChar = _NextNonIgnoringChar();

	NotationDict.DefaultValue = STR_ROOT;
	__BuildTree(fIsNotationCaseSensitive, fIsContentCaseSensitive);

	fclose(FIN);
	FIN = NULL;
}

void CxmlTree::OutputToStream(ostream& outStream, int StepInto)
{
	Root->OutputTreeToStream(outStream,StepInto);
}

void CxmlTree::__BuildTree(bool fIsNotationCaseSensitive, bool fIsContentCaseSensitive)
{
	this->fIsNotationCaseSensitive = fIsNotationCaseSensitive;
	this->fIsContentCaseSensitive = fIsContentCaseSensitive;

	CxmlTreeNode* pNode;
	int tokenType;
	string token;
	int t;

	Root = new CxmlTreeNode;
	Root->pNotationName = &(STR_ROOT);

	pNode = Root;
	while (!fEOF)
	{
		tokenType = _NextToken(token);
		switch (tokenType)
		{
		case 1:
			{
				if (!fIsNotationCaseSensitive)
					CDataStructureTools::ToLowerCase(token);
				t = NotationDict.GetID(token, true);
				pNode = pNode->GetNewChild();
				pNode->NotationID = t;
				pNode->pNotationName = NotationDict.GetValue(t);
				break;
			}
		case -1:
			{
				if (!fIsNotationCaseSensitive)
					CDataStructureTools::ToLowerCase(token);
				t = NotationDict.GetID(token, true);
				if (t == pNode->NotationID)
				{
					pNode = pNode->pParent;
				}
				break;
			}
		case 0:
			{
				if (!fIsContentCaseSensitive)
					CDataStructureTools::ToLowerCase(token);
				pNode->strContent += token;
				break;
			}
		default:
			{
				break;
			}
		}
	}
}

CxmlTreeNode* CxmlTree::GetFirstNode_PreorderTraversal()
{
	return Root;
}

CxmlTreeNode* CxmlTree::GetFirstNode_PostorderTraversal()
{
	CxmlTreeNode* pNode;
	for (pNode=Root;pNode && pNode->pFirstChild;)
		pNode = pNode->pFirstChild;
	return pNode;
}

